from PyQt4.QtGui import *
from PyQt4.QtCore import *
from core.util import first_item, isiter, TypeIter
from core import cmds
from mimetype import PyMimeData
from core.base import ComponentBase, DataHandle, ComponentRenamed



class SelectionModel(QItemSelectionModel):
    def __init__(self, model):
        QItemSelectionModel.__init__(self, model)
        
        self.connect(self, SIGNAL("currentChanged(const QModelIndex&,const QModelIndex&)"), self.on_current_change)
        
    def on_current_change(self, curr, prev):
        item = curr.model().itemFromIndex(curr)
        data = item.data_obj
        cmds.select(data)
    
class Item(QStandardItem):
    def __init__(self, data):
        QStandardItem.__init__(self)
        self.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.setEditable(False)
        self.setText(str(data))
        self.data_obj = data
        
    def get_data(self):
        return self.data_obj
    
    def get_editor(self):
        pass
        
class NodeItem(Item):
    ''' Qt items for Components / Nodes '''
    
    def __init__(self, data):
        Item.__init__(self, data)
        self.setEditable(True)
        
    def data(self, role):
        if role == Qt.EditRole:
            return QVariant(self.data_obj.name)
        elif role == Qt.DisplayRole:
            return QVariant(str(self.data_obj))
        return Item.data(self, role)
    
    def setData(self, data, role):
        if role == Qt.EditRole:
            self.data_obj.name = str(data.toString())
            return
        Item.setData(self, data, role)
    
class TypeItem(Item):
    ''' Qt Items for types (Component Classes) '''

class DataHandleItem(Item):
    ''' Qt items for DataHandles '''
    def __init__(self, data):
        Item.__init__(self, data)
        self.setText(str(data.get()))
        data.attr.change.connect(self.on_data_change)
        
    def on_data_change(self, *args, **kw):
        #self.setText(str(self.data_obj.get()))
        self.emitDataChanged()
        
    def get_editor(self):
        self.editor = None
        self.editor_instances = []
        
        self.editor = cmds.editor(self.data_obj)
        if not self.editor is None:
            # I have to store the editor instance, or it will get deleted
            # Gotta find a better way to do it.
            
            editor_inst = self.editor(self.data_obj)
            self.editor_instances.append(editor_inst)
            return editor_inst.widget
        
        std_editor = QLabel()
        std_editor.setText(str(self.text()))
        return std_editor


def DataItem(*args):
    ''' Constructs a item based on the type of input.
        Will construct a standard item (Item) if no other item matches the input.
    '''

    if len(args) == 0:
        return Item(None)
    elif len(args) == 1:
        data = args[0]
        if cmds.is_type(data):
            return TypeItem(data)
        elif cmds.is_component(data):
            return NodeItem(data)
        elif isinstance(data, DataHandle):
            return DataHandleItem(data)
        else:
            return Item(data)
    elif len(args) == 2:
        if cmds.is_type(args[0]) and cmds.is_component(args[1]):
            return DataHandleItem(DataHandle(*args))
    else:
        return Item(args)
    
    
    
         
    
        
    
class NIModel(QStandardItemModel):
    def __init__(self):
        QStandardItemModel.__init__(self)
 
    
    def mimeTypes(self):
        types = QStringList()
        types.append('text/plain')
        return types
    
    
    def mimeData(self, index):
        item = self.itemFromIndex(index[0])
        mimeData = PyMimeData(item.data_obj)
        print 'mimeData', item.data_obj, mimeData
        return mimeData
    
    
    def dropMimeData(self, mimedata, action, row, column, parent):
        print 'DROPPING'
        data = str(mimedata.text())
        
        comp = cmds.object_from_string(data)
        
        if cmds.is_type(comp):
            comp = comp()
        
        
        item = self.itemFromIndex(parent)
        if item is None:
            comp.parent = item
        else:
            comp.parent = item.get_data()
        return True
        
    def move_item(self, item, new_parent):
        if item is new_parent:
            return
        print 'MOVING ITEM', item, new_parent, item.get_data()
        items = self.item_parent(item).takeRow(item.row())
        if new_parent is None:
            self.appendRow(items)
        else:
            new_parent.appendRow(items)
    
    def item_from_data(self, data):
        for item in self.items():
            if item.get_data() is data:
                return item
    
    def add(self, nodes, parent=None):
        if not hasattr(nodes, '__iter__'):
            nodes = [nodes]
        
        if parent is None:
            parent = self.invisibleRootItem()
        
        result = []
        for node in nodes:
            qitem = DataItem(node)
            parent.setChild(parent.rowCount(), 0, qitem)
            result.append(qitem)
            
        return result
    
    def items(self, root=None):
        if root is None:
            root = self.invisibleRootItem()
        for row in range(root.rowCount()):
            child = root.child(row)
            yield child
            for item in self.items(child):
                yield item
    
    def walk_rows(self, root=None):
        if root is None:
            root = self.invisibleRootItem()
        for row in root.rowCount():
            yield root, row
    
    def item_parent(self, item):
        parent = item.parent()
        if parent is None:
            parent = self.invisibleRootItem()
        return parent
    
    def add_column(self, items, func, pos):
        if not callable(func): return
        
        for item in items:
            data = item.get_data()
            col_data = func(data)
            col_item = DataItem(col_data)
            self.item_parent(item).setChild(item.row(), pos, col_item)
    
    def group(self, items, func):
        if not callable(func): return
        
        group_items = {}
        for item in items:
            data = item.get_data()
            group = func(data)
            if group is None:
                continue
            groupitem = group_items.get(group)
            
            if groupitem is None:
                groupitem = DataItem(group)
                parent = self.invisibleRootItem()
                parent.setChild(parent.rowCount(), 0, groupitem)
                group_items[group] = groupitem
            
            itemparent = item.parent()
            if itemparent is None: itemparent = self.invisibleRootItem()
            item = itemparent.takeRow(item.row())
            
            groupitem.appendRow(item)
                
                    


class QListEditorModel(QStandardItemModel):
    def __init__(self):
        QStandardItemModel.__init__(self)
        
    def set_items(self, items):
        self.clear()
        self._set_list_items(items, self.invisibleRootItem())
        
    def _set_list_items(self, L, parent):
        last_group = parent
        for item in L:
            if isiter(item):
                self._set_list_items(item, last_group)
            
            q_item = DataItem(item)
            parent.setChild(parent.rowCount(), 0, q_item)
            
            last_group = q_item
            
        quickset_item = QStandardItem()
        parent.setChild(parent.rowCount(), 0, quickset_item)


class QListModel(QStandardItemModel):
    def __init__(self):
        QStandardItemModel.__init__(self)
        self.group_fn = None
        self._data = []
        self.relations = []
        self.headers = []
        
    def mimeTypes(self):
        types = QStringList()
        types.append("text/plain");
        return types;

    def mimeData(self, index):
        item = self.itemFromIndex(index[0])
        mimeData = PyMimeData(item.data_obj)
        return mimeData
        
    def set_data(self, data):
        self.clear()
        parent = self.invisibleRootItem()
        for item in data:
            q_item = DataItem(item)
            parent.setChild(self.rowCount(), 0, q_item)
        
    def walk_list(self, root, L):
        if not isiter(L):
            yield (root, L)
        else:
            last_item = None
            for item in L:
                yield (root, item)
                for sub_item in self.walk_list(last_item, item):
                    yield (item, sub_item)
                last_item = item
        
    def set_item(self, node, parent_qitem, parent_items):
        node_handler = tree_node
        parent_node = tree_node.parent
        
        parent_iter = TypeIter(parent_items[::-1])
        
        #try:
        #    items = list(node_handler.item(parent_iter))
        #except Exception, e: 
        #    print('Error in function: ' + str(e))
        #    return
        
        items = node_handler
        
        if not isiter(items): items = [items]
        
        for item in items:
            qitem = self._add_data_item(item, parent_qitem)
             
            for child_node in tree_node.children:
                self.set_item(child_node, qitem, parent_items + [item])
            
        
    def update(self):
        self.clear()
        group_items = {}
        self.setColumnCount(1 if not self.relations else len(self.relations))
        if len(self.headers) > 0:
            self.setHorizontalHeaderLabels(self.headers)
        if not self._data: return
        
        last_parent = None
        last_qitem = self.invisibleRootItem()
        q_parent = self.invisibleRootItem()
        for node in self._data:
            q_item = DataItem(node)
            q_parent.setChild(self.rowCount(), 0, q_item)
        
        self.emit(SIGNAL("modelReset"))
    
    def items(self, root=None):
        if root is None:
            root = self.invisibleRootItem()
        for row in range(root.rowCount()):
            for col in range(root.columnCount()):
                yield root.child(row, col)
            child = root.child(row)
            for item in self.items(child):
                yield item
    
    def has_data(self, data):
        for item in self.items(None):
            if not item: continue
            if item.data_obj is data:
                return item
    
    def _add_data_item(self, data, parent):
        existing = self.has_data(data)
        if existing: return existing
        group_obj = None
        if isiter(self.group_fn):
            try: 
                group_obj = [first_item(fn(data)) for fn in self.group_fn]
            except Exception, e: 
                print("FAIL " + str(e)) 
        if group_obj and group_obj[0]:
            parent = self._add_data_item(group_obj[0], parent)
        
        if parent is None: return
        row_count = parent.rowCount()
        if not self.relations:
            q_item = DataItem(data)
            parent.setChild(row_count, 0, q_item)
            return q_item
        
        first = None
        for index, rel in enumerate(self.relations):
            rel_data = None
            try:
                rel_data = rel(data)
            except:
                pass
            if rel_data is None: continue
            rel_item = DataItem(rel_data)
            parent.setChild(row_count, index, rel_item)
            if index == 0: first = rel_item
        return first
       
ListModel = QListModel         

class AttrItem(QStandardItem):
    def __init__(self, component, attr):
        QStandardItem.__init__(self)
        self.component = component
        self.attr = attr
        
    def data(self, role):
        if role == Qt.DisplayRole:
            return QVariant(self.attr.name)
        return QStandardItem.data(self, role)

class DataModel(QStandardItemModel):
    def __init__(self):
        QStandardItemModel.__init__(self)
        self.component = None
        self.data_handles = []
        self.clear()
        
    def set_component(self, component):
        if self.component is not None:
            cls = self.component.__class__
            cls.change.disconnect(self.on_class_change)
            self.component.change.disconnect(self.on_component_change)
        self.component = component
        cls = self.component.__class__
        cls.change.connect(self.on_class_change)
        
        
        
    def clear(self):
        QStandardItemModel.clear(self)
        self.input_parent = QStandardItem()
        #self.input_parent.setExpanded(True)
        self.input_parent.setText("Input Data")
        
        self.output_parent = QStandardItem()
        #self.output_parent.setExpanded(True)
        self.output_parent.setText("Output Data")
        
        self.invisibleRootItem().setChild(0, 0, self.input_parent)
        self.invisibleRootItem().setChild(1, 0, self.output_parent)
        self.invisibleRootItem().setChild(0, 1, QStandardItem())
        self.invisibleRootItem().setChild(1, 1, QStandardItem())
        self.setHorizontalHeaderLabels(["Name", "Data"])
        
        
        
    def set_input_data(self, component, attributes):
        self.set_data(self.input_parent, component, attributes)
        
    def set_output_data(self, component, attributes):
        self.set_data(self.output_parent, component, attributes, False)
        
    def set_data(self, parent, component, attributes, open_editor=True):
        #self.removeRows(0, parent.rowCount(), self.indexFromItem(parent))
        #parent.clear()
        self._add_data(parent, component, attributes, open_editor)
    
    def on_attr_added(self, object, attr):
        print 'ATTR ADDED', object, attr
        handle = DataHandle(object, attr)
        qitem = DataItem(handle)
        qitem.setEditable(True)
        parent_item = self.item_from_attr(attr.parent)
        attr_item = AttrItem(object, attr)
        next_row = parent_item.rowCount()
        #handle.attr_added.connect(self.on_attr_added)
        #object.attr_removed.connect(self.on_attr_removed)
        parent_item.setChild(next_row, 0, attr_item)
        parent_item.setChild(next_row, 1, qitem)
        
    def on_attr_removed(self, object, attr):
        print 'ATTR REMOVED'
        item = self.item_from_attr(attr)
        item.parent().removeRow(item.row())
        
    def item_from_attr(self, attr):
        for item in self.items():
            if not isinstance(item, AttrItem):
                continue
            if item.attr.name == attr.name:
                return item
     
    def _add_data(self, parent_item, component, attrs, open_editor=True):
        #print 'handles', list(handles)
        for attr in attrs:
            #if not isinstance(data, DataHandle):
            #    print 'Data Model: Data is not a DataHandle', str(data)
            #    continue
            name = attr.name
            data_handle = DataHandle(component, attr)
            data = data_handle.get()
            qitem = DataItem(data_handle)
            if open_editor:
                qitem.setEditable(True)
            #if open_editor:
            #    index = self.indexFromItem(qitem)
            #    self.openPersistentEditor(index)
            nameitem = AttrItem(component, attr)
            rowcount = parent_item.rowCount()
            parent_item.setChild(rowcount, 0, nameitem)
            parent_item.setChild(rowcount, 1, qitem)
            
            if not data_handle.is_expression() and attr.has_children(data):
                data.attr_added.disconnect(self.on_attr_added)
                data.attr_removed.disconnect(self.on_attr_removed)
                data.attr_added.connect(self.on_attr_added)
                data.attr_removed.connect(self.on_attr_removed)
                self._add_data(nameitem, data, data.attrs(), open_editor)
                
    def items(self, root=None):
        if root is None:
            root = self.invisibleRootItem()
        for row in range(root.rowCount()):
            for col in range(root.columnCount()):
                yield root.child(row, col)
            child = root.child(row)
            for item in self.items(child):
                yield item
            
            
        
        
    
    
                